#ifndef FS_H
#define FS_H

#include "main.h"
#include "settings.h"
#include "structs.h"
#include "string.h"
#include "memory.h"
#include "print.h"

/** TREIBER GUIDE
	Wie ein VFS Treiber auszusehen hat. Ein VFS Treiber stellt mehrere Funktionen bereit,
	die aufgerufen werden, sobald etwas mit einem Eintrag des Treibers passiert (zB wenn
	eine Datei des Treibers geöffnet wird. Die Prototypen sehen wie folgt aus:
	BOOL handler_init (char driver_number);
	BOOL handler_deinit ();
	FILE *handler_open (const char *path, struct fs_entry *entry, const char *params);
	int handler_close (FILE *handle);
	size_t handler_read (FILE *handle, char *buffer, size_t bytes);
	size_t handler_write (FILE *handle, void *buffer, size_t bytes);
	int handler_seek (FILE *handle, int offset, int origin);
	size_t handler_tell (FILE *handle);
	BOOL handler_remove (const char *path);
	BOOL handler_create (const char *path, char flags);
	handle_t handler_list_start (const char *path);
	struct fs_entry *handler_list (handle_t handle);
	BOOL handler_list_end (handle_t handle);
	BOOL handler_copy (const char *path, const char *newpath);
	BOOL handler_move (const char *path, const char *newpath);

	Zu beachten ist, dass die Pfad angaben bereits mit fs_translate_path
	bearbeitet wurde und dass diese Pointer NICHT per free freigegeben werden!
	Die open und close Funktionen übernehmen das Management der FILE structs,
	alloziieren Speicher für diese und geben diesen auch wieder frei.
	Weiter muss ein Treiber nicht alle Funktionen zur verfügung stellen,
	auch wenn dies natürlich wünschenswert ist ;)
**/

// Fehlernummern
#define FS_ERR_SUCCESS         1 // Alles OK
#define FS_ERR_EOF	      -1 // End of File
#define FS_ERR_ALREADY_EXISTS -2 // Es gibt bereits eine Datei/Ordner mit diesem Namen
#define FS_ERR_NO_PERMISSION  -3 // Keine Rechte
#define FS_ERR_WORKING_DIR    -4 // Das Arbeitsverzeichniss exestiert nicht
#define FS_ERR_NO_NODES       -5 // Es gibt keine Knotenpunkte
#define FS_ERR_NULL	      -6 // Es wurde ein Null-Pointer übergeben
// Flags
#define FS_FLAG_FOLDER	0x01	// Ordner?
#define FS_FLAG_SYSTEM  0x02	// Systemdatei?
#define FS_FLAG_VIRTUAL 0x04	// Virtuelle Datei?

#define DIR_MAGIC	0x1CAFFEE1

// fseek Flags
#define SEEK_SET 1	// Vom Anfang der Datei
#define SEEK_CUR 2	// Relativ zur aktuellen Position
#define SEEK_END 3	// Vom Ende der Datei

#define FS_ACC_WRITE  0x01	// Schreibender Zugriff
#define FS_ACC_READ   0x02	// Lesender Zugriff
#define FS_ACC_APPEND 0x04	// Schreibender Zugriff (hinzufügend)
// Parameter für einen Treiber
#define FS_ARG_OPEN  1
#define FS_ARG_CLOSE 2
#define FS_ARG_READ  3
#define FS_ARG_WRITE 4

// Hilfsstruct
struct fs_path {
	char *path;
	short len;
	BOOL last;
};

// Treiberstrukt
struct fs_driver {
	BOOL (*handler_init)(char);						// Initialisiert den Treiber
	FILE *(*handler_open)(const char*, struct fs_entry*, const char*);	// Datei öffnen
	int (*handler_close)(FILE*);						// Datei schliessen
	size_t (*handler_read)(FILE*, char*, size_t);				// Datei lesen
	size_t (*handler_write)(FILE*, void*, size_t);				// In Datei schreiben
	int (*handler_seek)(FILE *, int, int);					// Position in Datei suchen
	size_t (*handler_tell)(FILE *);						// Aktuelle Position in Datei
	BOOL (*handler_remove)(const char*);					// Datei oder Ordner löschen
	BOOL (*handler_create)(const char*, char);				// Datei oder Ordner erstellen
	handle_t (*handler_list_start)(const char*);				// Ordnerihalt auslesen starten
	BOOL (*handler_list_end)(handle_t);					// auslesen beenden
	struct fs_entry*(*handler_list)(handle_t);				// Aktuellen Eintrag lesen
	BOOL (*handler_copy)(const char*, const char*);				// Datei/Ordner kopieren
	BOOL (*handler_move)(const char*, const char*);				// Datei/Ordner umbenennen/verschieben
	BOOL (*handler_deinit)();						// Stoppt den Treiber
};

/* Initialisiert das Dateisystem
 */
void fs_init ();

/* Hilfsfunktion für fs_translate_path
 */
struct fs_path fs_path_helper (char *);

/* Erstellt einen kompletten Pfad aus einem relativen Pfad
 * Parameter: Pfad
 * Rückgaben: Kompletter Pfad (wieder per free freigeben!!)
 */
char *fs_translate_path (const char *);

/* Sucht eine Datei/Ordner
 * Parameter: Name
 * Rückgaben: Bei Erfolg einen Pointer auf den Eintrag, ansonsten NULL
 */
struct fs_entry *fs_find_entry (const char *);

/* Versucht eine Datei/Ordner zu finden
 * Parameter: Ziel
 * Rückgaben: TRUE wenn die Datei/Ordner exestiert
 */
BOOL fs_exists (const char *);

/* Erstellt einen Dateiknoten
 * Parameter: Dateiname, Treiber ID
 * Rückgaben: Bei Erfolg 1, sonst Fehlernummer
 */
int fs_create_file (const char *, char);

/* Erstellt einen Verzeichnissknoten
 * Parameter: Ordnername, Treiber ID
 * Rückgaben: Bei Erfolg 1, sonst Fehlernummer
 */
int fs_create_folder (const char *, char);

/* Fügt einen Knoten in die Liste ein
 * Parameter: Name des Knoten, Flags, Elternknoten, Treiber
 * Rückgaben: Pointer auf den Knoten
 */
struct fs_entry *fs_add_node (const char *, char, struct fs_entry*, char);

/* Trägt einen Treiber in die Liste ein
 * Parameter: Treiber Handler Funktionen (Sollte selbsterklärend sein)
 * Rückhgaben: Treiber ID, bei Fehler 0xFF
 */
char fs_announce_driver (
		BOOL (*h_init)(char),
		FILE *(*h_open)(const char*, struct fs_entry*, const char*),
		int (*h_close)(FILE*),
		size_t (*h_read)(FILE*, char*, size_t),
		size_t (*h_write)(FILE*, void*, size_t),
		int (*h_seek)(FILE *, int, int),
		size_t (*h_tell)(FILE *),
		BOOL (*h_remove)(const char*),
		BOOL (*h_create)(const char*, char),
		handle_t (*h_list_start)(const char*),
		BOOL (*h_list_end)(handle_t),
		struct fs_entry*(*h_list)(handle_t),
		BOOL (*h_copy)(const char*, const char*),
		BOOL (*h_move)(const char*, const char*),
		BOOL (*h_deinit)()
		);

/* Setzt das aktuelle Arbeitsverzeichniss
 * Parameter: Neues Arbeitsverzeichniss
 * Rückgaben: Keine
 */
void fs_set_working_dir (const char *);

/* Gibt das aktuelle Arbeitsverzeichniss zurück
 * Parameter: Zeiger auf einen Puffer, Länge des Puffers
 * Rückgaben: Bei Erfolg Zeiger auf buf, ansonsten NULL
 */
char *fs_get_working_dir (char *, uint32_t);

/* Parst die Dateiparameter, die an die fopen Funktion übergeben werden
 * Parameter: Parameter
 * Rückgaben: Bitmaske der Parameter
 */
char fs_parse_params (const char *);

/* Gibt den ersten Eintrag im Dateisystem zurück (Normalerweise "/")
 * Parameter: Keine
 * Rückgaben: Zeiger auf den ersten Eintrag
 */
struct fs_entry *fs_get_first_entry ();

/** Wrapper Funktionen - Die f... Funktionen **/

/* Öffnet eine Datei
 * Parameter: Dateiname, flags
 * Rückgaben: Bei Erfolg einen Pointer auf eine FILE Struktur, ansonsten NULL
 */
FILE *fopen (const char *, const char *);

/* Schliesst einen Dateihandle
 * Parameter: Dateihandle
 * Rückgaben: Bei Erfolg ungleich FS_ERR_EOF
 */
int fclose (FILE *);

/* Liest aus einem Dateihandle
 * Parameter: Ausgabepuffer, Elementengröße, Element anzahl, Dateihandle
 * Rückgaben: Anzahl gelesener Bytes
 */
size_t fread (void *, size_t, size_t, FILE *);

/* Schreibt in einen Dateihandle
 * Parameter: Eingabepuffer, Elementgröße, Element anzahl, Dateihandle
 * Rückgaben: Geschriebene Bytes
 */
size_t fwrite (void *, size_t, size_t, FILE *);

/* Setzt die aktuelle Position in einem Dateihandle
 * Parameter: Dateihandle, Offset, Herkunft (siehe SEEK_SET, SEEK_CUR und SEEK_END)
 * Rückgaben: Bei Erfolg 0, ansonsten ungleich 0
 */
int fseek (FILE *, int, int);

/* Gibt die aktuelle Position in einer Datei zurück
 * Parameter: Dateihandle
 * Rückgaben: Position in der Datei
 */
int ftell (FILE *);

/* Öffnet ein Verzeichniss um sein Inhalt auszulesen
 * Parameter: Pfad zum Verzeichniss
 * Rückgaben: Bei Erfolg einen Zeiger auf eine DIR Struktur, ansonsten NULL
 */
DIR *opendir (const char *);

/* Liest den nächsten Eintrag aus einem Verzeichniss aus
 * Parameter: Zeiger auf eine DIR Struktur
 * Rückgaben: Zeiger auf eine dirent Struktur
 */
dirent *readdir (DIR *);

/* Setzt die Position im Verzeichniss zurück
 * Parameter: Zeiger auf eine DIR Struktur
 * Rückgaben; Keine
 */
void rewinddir (DIR *);

/* Schliesst einen Ordnerhandle
 * Parameter: Zeiger auf eine DIR Struktur
 * Rückgaben: Bei Erfolg 0, ansonsten ungleich 0
 */
int closedir (DIR *);

#endif
